﻿// Decompiled with JetBrains decompiler
// Type: Netick.Unity.NetworkObject
// Assembly: Netick.Unity, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E128C7FD-39ED-4452-86AC-6EC748457030
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.Unity.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.Unity.xml

using System;
using System.Collections.Generic;
using System.Numerics;
using UnityEngine;
using UnityEngine.Serialization;

#nullable disable
namespace Netick.Unity;

/// <summary>
/// Any GameObject which needs to be synced/replicated must have a Network Object component. It’s the component that tells Netick a GameObject is networked. Although not every child GameObject needs it, only the root has to have a Network Object.
/// </summary>
[AddComponentMenu("Netick/Network Object")]
[DisallowMultipleComponent]
[SerializeField]
[Serializable]
public class NetworkObject : MonoBehaviour, INetickEntity
{
  internal NetworkScene Scene;
  internal NetworkTransform AttachedNetworkTransform;
  internal List<NetworkEventsListener> EventListeners = new List<NetworkEventsListener>(2);
  [SerializeField]
  [Range(0.0f, 4f)]
  internal int _AoILayer;
  [SerializeField]
  internal BroadPhaseFilter _BroadPhaseFilter;
  [SerializeField]
  internal bool _NarrowPhaseFilter;
  [SerializeField]
  internal bool _SpatialPrioritization;
  [SerializeField]
  [FormerlySerializedAs("_predictionMode")]
  internal Relevancy _PredictionMode;
  [SerializeField]
  [FormerlySerializedAs("AddToNetworkLoop")]
  internal bool _AddToNetworkLoop = true;
  [SerializeField]
  [FormerlySerializedAs("_isPersistent")]
  internal bool _IsPersistent;
  internal int ParentId = -1;
  [SerializeField]
  internal NetworkObject PrefabRoot;
  [SerializeField]
  internal NetworkObject[] Children = new NetworkObject[0];
  [SerializeField]
  internal int PrefabIndex = -1;
  [SerializeField]
  private int _PrefabId = -1;
  [SerializeField]
  internal int _SceneId = -1;
  [SerializeField]
  internal int _RuntimeSceneId = -1;
  internal bool IsPrefabInstance;

  /// <summary>
  /// The <see cref="T:Netick.Unity.NetworkSandbox" /> this <see cref="T:Netick.Unity.NetworkObject" /> belongs to.
  /// </summary>
  public NetworkSandbox Sandbox { get; private set; }

  /// <summary>
  /// The <see cref="T:Netick.NetickEngine" /> this <see cref="T:Netick.Unity.NetworkObject" /> belongs to.
  /// </summary>
  public NetickEngine Engine { get; internal set; }

  /// <summary>
  /// The <see cref="T:Netick.Entity" /> this <see cref="T:Netick.Unity.NetworkObject" /> represents in the Netick simulation.
  /// </summary>
  public Entity Entity { get; private set; }

  /// <summary>
  /// Returns a network-serializable reference to this <see cref="T:Netick.Unity.NetworkObject" />. This is used to get a networkable identifier for this object, which can be used for RPCs or network properties.
  /// </summary>
  /// <returns></returns>
  public NetworkObjectRef GetRef() => new NetworkObjectRef(this);

  /// <summary>
  /// Returns the unique identifier of this <see cref="T:Netick.Unity.NetworkObject" /> in the Netick simulation. This id is assigned by the Netick when the object is added to the simulation and is used to identify the object across the network.
  /// </summary>
  public int Id => this.Entity.Id;

  internal int RawId => this.Entity == null ? -1 : this.Entity.RawId;

  /// <summary>
  /// Returns the tick at which this <see cref="T:Netick.Unity.NetworkObject" /> was spawned locally. This is a local value, and is not synced.
  /// </summary>
  public Tick SpawnTick { get; private set; }

  /// <summary>
  /// Returns true if this <see cref="T:Netick.Unity.NetworkObject" /> has been added to the simulation by Netick, and thus has a valid id.
  /// </summary>
  [SerializeField]
  public bool HasValidId => this.Entity != null && this.Entity.Id > -1;

  /// <summary>
  /// <i><b>[Server Only]</b></i> Returns true if <see cref="M:Netick.Unity.NetworkSandbox.Destroy(Netick.Unity.NetworkObject)" /> has been called on this object in the server, and the object is scheduled to be removed from the simulation (destroyed) at the end of the current tick.
  /// </summary>
  public bool IsMarkedForDestruction => this.Entity.IsMarkedForDestruction;

  /// <summary>
  /// Returns the <see cref="T:Netick.Unity.NetworkBehaviour" /> components attached to this <see cref="T:Netick.Unity.NetworkObject" />.
  /// </summary>
  public NetworkBehaviour[] NetworkedBehaviours { get; internal set; } = new NetworkBehaviour[0];

  /// <summary>
  /// Returns the <see cref="T:Netick.Unity.NetickBehaviour" /> components attached to this <see cref="T:Netick.Unity.NetworkObject" />.
  /// </summary>
  public NetickBehaviour[] NetickBehaviours { get; internal set; } = (NetickBehaviour[]) new NetworkBehaviour[0];

  public int AoILayer => this._AoILayer;

  public BroadPhaseFilter BroadPhaseFilter => this._BroadPhaseFilter;

  public bool NarrowPhaseFilter => this._NarrowPhaseFilter;

  public Relevancy PredictionMode => this._PredictionMode;

  public bool SpatialPrioritization => this._SpatialPrioritization;

  public bool IsPersistent => this._IsPersistent;

  /// <summary>
  /// The <see cref="T:Netick.Unity.NetworkObject" /> parent of this object.
  /// </summary>
  public NetworkObject Parent { get; private set; }

  internal bool IsPrefabChild
  {
    get
    {
      if (this.PrefabIndex < 0)
        return false;
      if (!this.IsSceneObject)
        return true;
      return (UnityEngine.Object) this.PrefabRoot != (UnityEngine.Object) null && this.PrefabRoot.IsPersistent;
    }
  }

  INetickNetworkScript[] INetickEntity.NetworkScripts
  {
    get => (INetickNetworkScript[]) this.NetworkedBehaviours;
  }

  INetickScript[] INetickEntity.AllScripts => (INetickScript[]) this.NetickBehaviours;

  Vector3 INetickEntity.WorldPosition => this.transform.position.ToNumerics();

  BroadPhaseFilter INetickEntity.BroadPhaseFilter => this._BroadPhaseFilter;

  bool INetickEntity.NarrowPhaseFilter => this._NarrowPhaseFilter;

  bool INetickEntity.UseSAP => this._SpatialPrioritization;

  bool INetickEntity.AddToNetworkLoop => this._AddToNetworkLoop;

  /// <summary>
  /// Returns true if this <see cref="T:Netick.Unity.NetworkObject" /> is a scene object (not a prefab instance). Scene objects are objects that are part of the scene and not instantiated from a prefab.
  /// </summary>
  public bool IsSceneObject => this._SceneId != -1;

  /// <summary>
  /// Returns true if this <see cref="T:Netick.Unity.NetworkObject" /> is a prefab object (not a scene object). Prefab objects are objects that are instantiated from a prefab and not part of the scene.
  /// </summary>
  public bool IsPrefabObject => !this.IsSceneObject && this.PrefabId != -1;

  /// <summary>
  /// Returns the prefab id of this <see cref="T:Netick.Unity.NetworkObject" />. This is used to identify the prefab from which this object was instantiated. If this object is a scene object, it returns -1.
  /// </summary>
  public int PrefabId
  {
    get => this._PrefabId;
    internal set => this._PrefabId = value;
  }

  /// <summary>
  /// Returns the scene id of this <see cref="T:Netick.Unity.NetworkObject" />. This value is only unique per-scene, and not per the entire game.
  /// </summary>
  public int SceneId
  {
    get => this._SceneId;
    internal set => this._SceneId = value;
  }

  /// <summary>
  /// Returns the runtime scene id of this <see cref="T:Netick.Unity.NetworkObject" />. This value is only unique per-scene, and not per the entire game.
  /// </summary>
  public int RuntimeSceneId
  {
    get => this._RuntimeSceneId;
    internal set => this._RuntimeSceneId = value;
  }

  /// <summary>
  /// Returns true if this <see cref="T:Netick.Unity.NetworkSandbox" /> is a client.
  /// </summary>
  public bool IsClient => this.Sandbox.IsClient;

  /// <summary>
  /// Returns true if this <see cref="T:Netick.Unity.NetworkSandbox" /> is the server.
  /// </summary>
  public bool IsServer => this.Sandbox.IsServer;

  /// <summary>
  /// Returns true if this <see cref="T:Netick.Unity.NetworkSandbox" /> is the owner of this Object. In this version of Netick: Server=Owner.
  /// </summary>
  public bool IsOwner => this.Sandbox.IsServer;

  /// <summary>
  /// Returns true if this <see cref="P:Netick.Unity.NetworkSandbox.LocalPlayer" /> is providing inputs for this <see cref="T:Netick.Unity.NetworkObject" />.
  /// </summary>
  public bool IsInputSource => this.Entity.IsInputSource;

  /// <summary>
  /// Returns true if we neither provide inputs nor own this <see cref="T:System.Object" />.
  /// </summary>
  public bool IsProxy => this.IsClient && !this.IsInputSource;

  /// <summary>
  /// Returns true if we are currently resimulating a previous input of the past. On the server, it always returns false since <b>only the clients resimulate</b>.
  /// </summary>
  public bool IsResimulating => this.Engine.IsResimulating;

  /// <summary>
  /// Returns the source <see cref="T:Netick.NetworkPlayer" /> (<see cref="T:Netick.NetworkPeer" />/<see cref="T:Netick.ServerConnection" />) of inputs for this <see cref="T:Netick.Unity.NetworkObject" />. If the source of inputs is remote (from a client) it returns that <see cref="T:Netick.ServerConnection" />, while on the
  /// input source itself it returns the local <see cref="T:Netick.NetworkPlayer" />.
  /// <para>Use <see cref="P:Netick.Unity.NetworkObject.InputSourcePlayerId" /> for the synced-to-everyone player id of the Input Source - because <see cref="P:Netick.Unity.NetworkObject.InputSource" /> is only valid in the client for objects the client itself is the input source of, and it's null for everything else.</para>
  /// </summary>
  public Netick.NetworkPlayer InputSource
  {
    get => this.Entity.InputSource;
    set => this.Entity.InputSource = value;
  }

  /// <summary>
  /// Returns the NetworkPlayerId of the input source of this object. This is synced to every client.
  /// </summary>
  public NetworkPlayerId InputSourcePlayerId => this.Entity.InputSourcePlayerId;

  /// <summary>
  /// <i><b>[Client Only]</b></i> On the client, returns true if this client is interested in this object. On the server, always returns true.
  /// </summary>
  public bool IsInterested => this.Entity.IsInterested;

  /// <summary>
  /// The interest group of this object. This can only be changed if BroadPhaseFilter is set to Custom.
  /// </summary>
  public InterestManagementGroup InterestGroup
  {
    get => this.Entity.InterestGroup;
    set => this.Entity.InterestGroup = value;
  }

  public void SetNarrowphaseInterest(Netick.NetworkPlayer player, bool isInterested)
  {
    this.Entity.SetNarrowphaseInterest(player, isInterested);
  }

  public void SetNarrowphaseInterest(NetworkPlayerId player, bool isInterested)
  {
    this.Entity.SetNarrowphaseInterest(this.Engine.GetPlayerById(player), isInterested);
  }

  /// <summary>
  /// Returns the networked state size of this object in bytes.
  /// </summary>
  public int StateSize => (int) this.Entity.StateSizeWords * 4;

  public EntityStateRef GetStateRef() => this.Entity.GetStateRef();

  [Obsolete("Use SceneId instead. This will be removed in the future.")]
  public int GetSceneId() => this._SceneId;

  internal void InitLocally(NetworkSandbox sandbox, NetworkScene scene)
  {
    this.Sandbox = sandbox;
    this.Scene = scene;
    this.AttachedNetworkTransform = this.GetComponent<NetworkTransform>();
    List<NetickBehaviour> netickBehaviourList = this.Sandbox.CachedNetickBehaviourList;
    List<NetworkBehaviour> networkBehaviourList = this.Sandbox.CachedNetworkBehaviourList;
    this.EventListeners.Clear();
    NetworkSandbox.GetBehaviours<NetickBehaviour>(sandbox, this.gameObject, netickBehaviourList, this.EventListeners, sandbox.Config.IncludeInactiveObjects);
    for (int index = 0; index < netickBehaviourList.Count; ++index)
    {
      if ((UnityEngine.Object) (netickBehaviourList[index] as NetworkBehaviour) != (UnityEngine.Object) null)
        networkBehaviourList.Add(netickBehaviourList[index] as NetworkBehaviour);
    }
    netickBehaviourList.Sort((Comparison<NetickBehaviour>) ((x, y) => this.Sandbox.Engine.GetBehaviourOrder(((object) x).GetType()).CompareTo(this.Sandbox.Engine.GetBehaviourOrder(((object) y).GetType()))));
    networkBehaviourList.Sort((Comparison<NetworkBehaviour>) ((x, y) => this.Sandbox.Engine.GetBehaviourOrder(((object) x).GetType()).CompareTo(this.Sandbox.Engine.GetBehaviourOrder(((object) y).GetType()))));
    this.NetickBehaviours = netickBehaviourList.ToArray();
    this.NetworkedBehaviours = networkBehaviourList.ToArray();
    netickBehaviourList.Clear();
    networkBehaviourList.Clear();
    for (int index = 0; index < this.NetickBehaviours.Length; ++index)
    {
      this.NetickBehaviours[index].Object = this;
      this.NetickBehaviours[index].Sandbox = sandbox;
    }
    if (this.IsSceneObject)
    {
      this.PrefabId = -1;
      this.PrefabRoot = (NetworkObject) null;
      this.PrefabIndex = -1;
      if (this.Children != null && this.Children.Length != 0)
        this.Children = new NetworkObject[0];
    }
    this.InitParentData();
  }

  unsafe void INetickEntity.Initialize(NetickEngine sandbox, Entity entity)
  {
    this.Engine = sandbox;
    this.Entity = entity;
    int* statePtr = entity.StatePtr;
    int num = 0;
    foreach (NetickBehaviour netickBehaviour in this.NetickBehaviours)
    {
      netickBehaviour.Entity = this.Entity;
      netickBehaviour.Object = this;
    }
    foreach (NetworkBehaviour networkedBehaviour in this.NetworkedBehaviours)
    {
      networkedBehaviour.BehaviourId = num;
      networkedBehaviour.StatePtr = statePtr;
      statePtr += networkedBehaviour.InternalGetStateSizeWords();
      ++num;
    }
  }

  internal int CalcPrefabSizeWords(NetworkSandbox sandbox)
  {
    List<NetickBehaviour> netickBehaviourList = sandbox.CachedNetickBehaviourList;
    int num = 0;
    bool activeInHierarchy = this.gameObject.activeInHierarchy;
    if (!activeInHierarchy)
      this.gameObject.SetActive(true);
    NetworkSandbox.GetBehaviours<NetworkBehaviour>(sandbox, this.gameObject, netickBehaviourList, (List<NetworkEventsListener>) null, sandbox.Config.IncludeInactiveObjects);
    for (int index = 0; index < netickBehaviourList.Count; ++index)
    {
      if ((UnityEngine.Object) (netickBehaviourList[index] as NetworkBehaviour) != (UnityEngine.Object) null)
        num += (netickBehaviourList[index] as NetworkBehaviour).InternalGetStateSizeWords();
    }
    netickBehaviourList.Clear();
    if (!activeInHierarchy)
      this.gameObject.SetActive(activeInHierarchy);
    return num;
  }

  void INetickEntity.NetworkAwake()
  {
    if (!this.IsClient || this.gameObject.activeInHierarchy || !this.IsPrefabInstance)
      return;
    this.gameObject.SetActive(true);
  }

  void INetickEntity.NetworkRegister()
  {
    if (!this.gameObject.activeInHierarchy && this.IsPrefabInstance)
      this.gameObject.SetActive(true);
    this.SpawnTick = this.Sandbox.AuthoritativeTick;
    this.InitParentData();
    foreach (NetworkEventsListener eventListener in this.EventListeners)
      this.Sandbox.Events.Subscribe(eventListener, this);
  }

  void INetickEntity.NetworkUnregister()
  {
    foreach (NetworkEventsListener eventListener in this.EventListeners)
      this.Sandbox.Events.Unsubscribe(eventListener);
  }

  /// <summary>
  /// Zeros out the networked state memory of this object. Ignores InputSource Relevancy.
  /// </summary>
  public unsafe void ClearState(bool invokeOnChanged = true)
  {
    int stateSizeWords = (int) this.Entity.StateSizeWords;
    int* source = stackalloc int[stateSizeWords];
    for (int index = 0; index < stateSizeWords; ++index)
      source[index] = 0;
    this.SetStateFrom((byte*) source, invokeOnChanged);
  }

  /// <summary>Copies the state of this object into target.</summary>
  public unsafe void CopyStateTo(byte* target)
  {
    MemoryAllocation.Copy((void*) target, (void*) this.Entity.StatePtr, (long) this.StateSize);
  }

  /// <summary>Copies the state of this object into target.</summary>
  public unsafe void CopyStateTo(byte[] target)
  {
    fixed (byte* target1 = target)
      this.CopyStateTo(target1);
  }

  public virtual unsafe void SetStateFrom(
    EntityStateRef stateRef,
    byte[] worldSnapshot,
    bool invokeOnChanged = true)
  {
    fixed (byte* worldSnapshot1 = worldSnapshot)
      this.SetStateFrom(stateRef, worldSnapshot1, invokeOnChanged);
  }

  public unsafe void SetStateFrom(
    EntityStateRef stateRef,
    byte* worldSnapshot,
    bool invokeOnChanged = true)
  {
    this.Entity.SetStateFrom(worldSnapshot + stateRef.StateOffsetBytes, 0, (int) this.Entity.StateSizeWords, invokeOnChanged);
    this.Entity.InvokeNetcodeIntoGameEngine();
  }

  /// <summary>
  /// Sets the state of this object. Ignores InputSource Relevancy.
  /// </summary>
  public unsafe void SetStateFrom(byte* source, bool invokeOnChanged = true)
  {
    this.Entity.SetStateFrom(source, 0, (int) this.Entity.StateSizeWords, invokeOnChanged);
    this.Entity.InvokeNetcodeIntoGameEngine();
  }

  /// <summary>
  /// Sets the state of this object. Ignores InputSource Relevancy.
  /// </summary>
  public unsafe void SetStateFrom(byte[] source, bool invokeOnChanged = true)
  {
    fixed (byte* source1 = source)
      this.SetStateFrom(source1, invokeOnChanged);
  }

  public void UpdateAoI() => this.UpdateAoI(this.transform.position);

  public unsafe void UpdateAoI(Vector3 position) => this.Entity.Move(*(Vector3*) &position);

  public T GetBehaviour<T>() where T : NetworkBehaviour
  {
    for (int index = 0; index < this.NetworkedBehaviours.Length; ++index)
    {
      if (this.NetworkedBehaviours[index] is T)
        return this.NetworkedBehaviours[index] as T;
    }
    return default (T);
  }

  public T GetBehaviourById<T>(int id) where T : NetworkBehaviour
  {
    return this.NetworkedBehaviours[id] as T;
  }

  internal void InitParentData()
  {
    this.Parent = (NetworkObject) null;
    if ((UnityEngine.Object) this.transform.parent != (UnityEngine.Object) null && (UnityEngine.Object) this.transform.parent.GetComponent<NetworkObject>() != (UnityEngine.Object) null)
    {
      this.Parent = this.transform.parent.GetComponent<NetworkObject>();
      this.ParentId = this.Parent.RawId;
    }
    else
      this.ParentId = -2;
  }

  internal void NetworkSetParent(int parentId)
  {
    Entity entity;
    this.Sandbox.Engine.AllEntities.TryGetValue(parentId, out entity);
    NetworkObject parent;
    switch (parentId)
    {
      case -2:
        return;
      case -1:
        parent = (NetworkObject) null;
        break;
      default:
        parent = entity?.UserEntity as NetworkObject;
        break;
    }
    this.InternalSetParent(parent);
  }

  /// <summary>
  /// <i><b>[Owner/InputSource Only]</b></i> Changes the parent of this object.
  /// </summary>
  /// <param name="obj">The object which will become the parent of this object.</param>
  public void SetParent(NetworkObject obj)
  {
    if (this.Engine.IsClient && !this.IsInputSource)
      NetickLogger.LogError(this.Entity, (object) "You must be either the owner or the input source of the object to be able to change its parent.");
    else if (this.IsPrefabChild)
      NetickLogger.LogError(this.Entity, (object) "You can't change the parent of a prefab child, only the root object.");
    else
      this.InternalSetParent(obj);
  }

  internal unsafe void InternalSetParent(NetworkObject parent)
  {
    if ((UnityEngine.Object) parent == (UnityEngine.Object) this.Parent)
      return;
    this.transform.parent = (UnityEngine.Object) parent != (UnityEngine.Object) null ? parent.transform : (Transform) null;
    this.ParentId = (UnityEngine.Object) parent != (UnityEngine.Object) null ? parent.RawId : -1;
    if (this.IsServer)
      this.Engine.SetEntityUserMetaDirty(this.Entity, (int*) &(*(NetworkObjectMeta*) this.Engine.GetEntityUserMeta(this.Entity) with
      {
        ParentId = this.ParentId
      }));
    this.Parent = parent;
  }

  public void SetInitialProperties(
    bool isPersistent,
    Relevancy predictionMode,
    bool addToNetworkLoop,
    BroadPhaseFilter broadPhaseFilter,
    bool narrowPhaseFilter)
  {
    this._IsPersistent = isPersistent;
    this._PredictionMode = predictionMode;
    this._AddToNetworkLoop = addToNetworkLoop;
    this._BroadPhaseFilter = broadPhaseFilter;
    this._NarrowPhaseFilter = narrowPhaseFilter;
  }

  private void Reset() => this.SceneId = UnityEngine.Random.Range(int.MinValue, int.MaxValue);
}
